common-helpers.ts 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. import { AllLang } from '@growi/core';
  2. import type { GetServerSidePropsContext, GetServerSidePropsResult } from 'next';
  3. import type { SSRConfig } from 'next-i18next';
  4. import type { CrowiRequest } from '~/interfaces/crowi-request';
  5. import { getLangAtServerSide } from '~/pages/utils/locale';
  6. export const NEXT_JS_ROUTING_PAGE = '[[...path]]';
  7. // Shared helper function to create i18n config with proper configuration
  8. export async function createNextI18NextConfig(
  9. context: GetServerSidePropsContext,
  10. namespacesRequired?: string[],
  11. preloadAllLang = false,
  12. ): Promise<SSRConfig> {
  13. const { serverSideTranslations } = await import('next-i18next/serverSideTranslations');
  14. // Import configuration to fix the error
  15. const nextI18NextConfig = await import('^/config/next-i18next.config');
  16. // Determine language from request context
  17. const req: CrowiRequest = context.req as CrowiRequest;
  18. const lang = getLangAtServerSide(req);
  19. // Prepare namespaces with commons as default
  20. const namespaces = ['commons'];
  21. if (namespacesRequired != null) {
  22. namespaces.push(...namespacesRequired);
  23. }
  24. else {
  25. // TODO: deprecate 'translation.json' in the future
  26. namespaces.push('translation');
  27. }
  28. // Call serverSideTranslations with proper configuration
  29. return serverSideTranslations(
  30. lang,
  31. namespaces,
  32. nextI18NextConfig,
  33. preloadAllLang ? AllLang : false,
  34. );
  35. }
  36. // Common user and redirect handling
  37. type RedirectResult = { redirect: { permanent: boolean; destination: string } };
  38. export function handleUserAndRedirects(context: GetServerSidePropsContext, props: Record<string, unknown>): RedirectResult | null {
  39. const req = context.req as CrowiRequest;
  40. const { user } = req;
  41. // Add current user if exists
  42. if (user != null) {
  43. props.currentUser = user.toObject();
  44. }
  45. // Check for redirect destination
  46. const redirectDestination = props.redirectDestination;
  47. if (typeof redirectDestination === 'string') {
  48. return {
  49. redirect: {
  50. permanent: false,
  51. destination: redirectDestination,
  52. },
  53. };
  54. }
  55. return null;
  56. }
  57. // Helper function to handle page data result with improved type safety
  58. export function handlePageDataResult<T, U>(
  59. result: GetServerSidePropsResult<T>,
  60. currentProps: U,
  61. ): GetServerSidePropsResult<U & T> {
  62. if ('redirect' in result) {
  63. return result as GetServerSidePropsResult<U & T>;
  64. }
  65. if ('notFound' in result) {
  66. return result as GetServerSidePropsResult<U & T>;
  67. }
  68. // Ensure result.props exists and is not a Promise
  69. if (!('props' in result) || !result.props) {
  70. throw new Error('Invalid page data result - missing props');
  71. }
  72. // Type-safe props merging
  73. return {
  74. props: {
  75. ...currentProps,
  76. ...result.props,
  77. } as U & T,
  78. };
  79. }
  80. // Type-safe GetServerSidePropsResult merger with overloads
  81. export function mergeGetServerSidePropsResults<T1, T2>(
  82. result1: GetServerSidePropsResult<T1>,
  83. result2: GetServerSidePropsResult<T2>,
  84. ): GetServerSidePropsResult<T1 & T2>;
  85. export function mergeGetServerSidePropsResults<T1, T2, T3, T4>(
  86. result1: GetServerSidePropsResult<T1>,
  87. result2: GetServerSidePropsResult<T2>,
  88. result3: GetServerSidePropsResult<T3>,
  89. result4: GetServerSidePropsResult<T4>,
  90. ): GetServerSidePropsResult<T1 & T2 & T3 & T4>;
  91. export function mergeGetServerSidePropsResults<T1, T2, T3, T4>(
  92. result1: GetServerSidePropsResult<T1>,
  93. result2: GetServerSidePropsResult<T2>,
  94. result3?: GetServerSidePropsResult<T3>,
  95. result4?: GetServerSidePropsResult<T4>,
  96. ): GetServerSidePropsResult<T1 & T2> | GetServerSidePropsResult<T1 & T2 & T3 & T4> {
  97. // Handle 2-argument case
  98. if (result3 === undefined && result4 === undefined) {
  99. if ('redirect' in result1) return result1;
  100. if ('redirect' in result2) return result2;
  101. if ('notFound' in result1) return result1;
  102. if ('notFound' in result2) return result2;
  103. if (!('props' in result1) || !('props' in result2)) {
  104. throw new Error('Invalid GetServerSidePropsResult - missing props');
  105. }
  106. return {
  107. props: {
  108. ...result1.props,
  109. ...result2.props,
  110. } as T1 & T2,
  111. };
  112. }
  113. // Handle 4-argument case
  114. if (result3 === undefined || result4 === undefined) {
  115. throw new Error('All 4 arguments required for 4-way merge');
  116. }
  117. if ('redirect' in result1) return result1;
  118. if ('redirect' in result2) return result2;
  119. if ('redirect' in result3) return result3;
  120. if ('redirect' in result4) return result4;
  121. if ('notFound' in result1) return result1;
  122. if ('notFound' in result2) return result2;
  123. if ('notFound' in result3) return result3;
  124. if ('notFound' in result4) return result4;
  125. if (!('props' in result1) || !('props' in result2)
  126. || !('props' in result3) || !('props' in result4)) {
  127. throw new Error('Invalid GetServerSidePropsResult - missing props');
  128. }
  129. return {
  130. props: {
  131. ...result1.props,
  132. ...result2.props,
  133. ...result3.props,
  134. ...result4.props,
  135. } as T1 & T2 & T3 & T4,
  136. };
  137. }
  138. // Type-safe property extraction helper
  139. export function extractTypedProps<T>(result: unknown, errorMessage: string): T {
  140. if (typeof result !== 'object' || result === null || !('props' in result)) {
  141. throw new Error(errorMessage);
  142. }
  143. return (result as { props: T }).props;
  144. }